//Alisa Burdeyny

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;
import java.util.Set;

import javax.media.opengl.*;
import javax.media.opengl.glu.GLU;
import javax.swing.*;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import com.sun.opengl.util.*;

public class AnimateField3D extends JFrame{

	JLayeredPane pane = new JLayeredPane();
	MainMenuPanel mainMenu = new MainMenuPanel();
	private JPanel animationPanel = new JPanel(new BorderLayout());
	DrawTopViewWalker person = new DrawTopViewWalker();
	boolean pressed[];
	Animator anim;
	Thread stepper;
	double lefTh, righTh, lefShi, righShi, lefArm, righArm, lefForarm, righForarm, posX, posZ, speed, walkAng;
	boolean lefThFor, righThFor, mainPanel, instructions, playPanel;
	private final int USER = 1;
	private final int HOME = 2;
	private final int AWAY = 3;
	private final int HOLDFRISBEE = 4;
	Field myField;
	Team currentTeam;
	HashSet<Player> players = new HashSet<Player>();
	Disc disc = new Disc();
	Vector3d vec;

	//add mouse and keyboard listeners
	private UserMouseInputListener userMouse;
	private UserKeyboardInputListener userKeyboard;

	GLCapabilities glcaps = new GLCapabilities(); //get capabilities for canvas
	final GLCanvas glCanvas = new GLCanvas(glcaps); //make new canvas

	public static void main(String args[]) {
		final AnimateField3D field = new AnimateField3D();
		field.setVisible(true);
		field.setResizable(false);
	}

	public AnimateField3D()
	{
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		glCanvas.addGLEventListener(new SimpleGLEventListener()); //add event listener
		glCanvas.addKeyListener(new SimpleKeyListener());    //add key listener
		glCanvas.addMouseListener(new SimpleMouseListener());
		mainMenu.addMouseListener(new SimpleMouseListener());
		addKeyListener(new SimpleKeyListener());
		addMouseListener(new SimpleMouseListener());

		anim = new Animator();  //add animator
		anim.add(glCanvas);
		stepper = new Thread(new StepThread());
		stepper.setName("Engine Stepper");
		anim.start();

		//initialize variables like angles, starting position, etc.		
		lefArm = 90;
		righArm = 90;
		lefForarm = 0;
		righForarm = 0;
		lefTh = 90;
		righTh = 90;
		lefShi = 90;
		righShi = 90;
		speed = 0;

		walkAng = 0;

		lefThFor = true;
		righThFor = false;
		mainPanel = true;
		instructions = false;
		playPanel = false;

		posX = 0;
		posZ = 0;

		pressed = new boolean[256];  //create array to check if certain keys are being pressed

		pane.setPreferredSize(new Dimension(800, 400));
		pane.addMouseListener(new SimpleMouseListener());
		pane.add(animationPanel);

		animationPanel.add(glCanvas);
		glCanvas.setSize(800,500);
		setSize(800,420);
		add(mainMenu);
	}

	private void initializeEngine()
	{		
		double speed = 1.0;
		double jump = 1.0;

		//Disc
		//Disc disc = new Disc();

		Set<Player> temp = new HashSet<Player>();

		//Home Team

		//User
		userMouse = new UserMouseInputListener(disc);
		userKeyboard = new UserKeyboardInputListener();
		Player user = new Player(new UserMovementRoutine(userMouse, userKeyboard), speed, jump);
		((UserMovementRoutine) user.routine).setUser(user);
		((UserMovementRoutine) user.routine).caughtDisc(disc);
		temp.add(user);

		for(int i = 0; i < 6; i++)
			temp.add(new Player(speed, jump));

		Team homeTeam = new Team(Field.HOME, Team.RIGHT, temp);

		//Away Team
		temp.clear();
		for(int i = 0; i < 7; i++)
			temp.add(new Player(speed, jump));

		Team awayTeam = new Team(Field.AWAY, Team.LEFT, temp);


		myField = new Field(disc, homeTeam, awayTeam, user);
		homeTeam.setField(myField);
		awayTeam.setField(myField);
		((UserMovementRoutine) user.routine).setField(myField);
		userMouse.setField(myField);

		disc.position = new Point3d(user.position.x, user.position.y, 1.0);

		//add listeners to the panels
		animationPanel.addMouseListener(userMouse);
		animationPanel.addMouseMotionListener(userMouse);
		animationPanel.addKeyListener(userKeyboard);
		glCanvas.addMouseListener(userMouse);
		glCanvas.addMouseMotionListener(userMouse);
		glCanvas.addKeyListener(userKeyboard);
		addMouseListener(userMouse);
		addMouseMotionListener(userMouse);
		addKeyListener(userKeyboard);

		if(!stepper.isAlive())
			stepper.start();

		long time = System.currentTimeMillis();
		while(System.currentTimeMillis() - time < 500l);
		
		if(!anim.isAnimating())
			anim.start();
	}

	public class SimpleGLEventListener implements GLEventListener
	{		
		//draw and animate the figure
		public void display(GLAutoDrawable drawable) 
		{
			GL gl = drawable.getGL();            // get JOGL Drawable object
			GLU glu = new GLU();
			GLUT glut = new GLUT();       

			int w = ((Component)drawable).getWidth();
			int h = ((Component)drawable).getHeight();
			int aspect = w/h;

			gl.glMatrixMode(GL.GL_PROJECTION);

			gl.glLoadIdentity();                //clears transformation matrix            
			glu.gluPerspective(60.0, aspect, 1.0, 500.0);     
			glu.gluLookAt(0, 250, 0,  1, 10, 1, 0, 1, 0); 

			gl.glMatrixMode(gl.GL_MODELVIEW);       

			gl.glEnable(GL.GL_DEPTH_TEST);
			gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

			//move left thigh
			if(lefTh > 65 && lefThFor == true)
			{
				lefTh-=.25*speed;
				if(lefTh <= 65)
					lefThFor = false;
			}
			else
			{
				lefTh+=.25*speed;
				if(lefTh >= 115)
					lefThFor = true;
			}

			//move right thigh (change angle)
			if(righTh < 115 && righThFor == false)
			{
				righTh+=.25*speed;
				if(righTh >= 115)
					righThFor = true;
			}
			else
			{
				righTh-=.25*speed;
				if(righTh <= 65)
					righThFor = false;
			}

			//move left shin (change angle)
			if(lefThFor == true && lefTh < 90 && lefShi > 70)
			{
				lefShi-= .75*speed;
			}
			else if(lefThFor == true && lefTh > 90)
			{
				lefShi+= .2*speed;
			}
			else if(lefThFor == false)
			{
				lefShi = lefTh+5*speed;
			}

			//move right shin (change angle)
			if(righThFor == true && righTh < 90 && righShi > 70)
			{
				righShi-= .75*speed;
			}
			else if(righThFor == true && righTh > 90)
			{
				righShi+= .2*speed;
			}
			else if(righThFor == false)
			{
				righShi = righTh+5*speed;
			}

			//move left arm
			if(lefThFor == true)
				lefArm+= .25*speed;
			else if(lefThFor == false)
				lefArm-= .25*speed;

			//move right arm
			if(righThFor == true)
				righArm+= .25*speed;
			else if(righThFor == false)
				righArm-= .25*speed;

			//get the home team
			currentTeam = myField.getHomeTeam();
			players = (HashSet<Player>) currentTeam.getPlayers();
			int i = 0;
			for (Player player : players)
			{
				//get the position of the player
				Point3d pos = player.position;
				posX = (pos.x)*2.9 - 145;
				posZ = (pos.y)*(225/50) - 85;

				//get the velocity vector of the player
				vec = player.velocity;
				walkAng = Math.atan2(vec.y, vec.x);

				//if player is holding disc, draw yellow
				if(player == myField.playerWithDisc)
				{
					person.MoveWalker(lefTh, righTh, lefShi, righShi, lefArm, righArm, lefForarm, righForarm);
					person.drawIt(drawable, posX, posZ, walkAng, HOLDFRISBEE);
				}
				//if player is the user and not holding disc, draw pinkish
				else if(player.routine instanceof UserMovementRoutine)
				{
					person.MoveWalker(lefTh, righTh, lefShi, righShi, lefArm, righArm, lefForarm, righForarm);
					person.drawIt(drawable, posX, posZ, walkAng, USER);
				}
				//if player is on home team but not holding frisbee and not the user, draw red
				else
				{
					person.MoveWalker(lefTh, righTh, lefShi, righShi, lefArm, righArm, lefForarm, righForarm);
					person.drawIt(drawable, posX, posZ, walkAng, HOME);
				}
			}

			//get away team
			currentTeam = myField.getAwayTeam();
			players = (HashSet<Player>) currentTeam.getPlayers();
			for (Player player : players)
			{
				//get position of player
				Point3d pos = player.position;
				posX = (pos.x)*2.9 - 145;
				posZ = (pos.y)*(225/50) - 85;

				//get velocity vector of player
				vec = player.velocity;
				walkAng = Math.atan2(vec.y, vec.x);

				//if player is holding disc, draw yellow
				if(player == myField.playerWithDisc)
				{
					person.MoveWalker(lefTh, righTh, lefShi, righShi, lefArm, righArm, lefForarm, righForarm);
					person.drawIt(drawable, posX, posZ, walkAng, HOLDFRISBEE);
				}
				//if player is on away team but not holding frisbee, draw blue
				else
				{
					person.MoveWalker(lefTh, righTh, lefShi, righShi, lefArm, righArm, lefForarm, righForarm);
					person.drawIt(drawable, posX, posZ, walkAng, AWAY);
				}
			}
			//get position of the disc
			Point3d pos = myField.disc.position;
			posX = (pos.x)*2.9 - 145;
			posZ = (pos.y)*(225/50) - 85;

			//draw the firsbee
			person.drawFrisbee(drawable, posX, posZ);
			gl.glLoadIdentity();

			//change variables based on keys the user is pressing
			moveStuff();
		}

		//initialize screen
		public void init(GLAutoDrawable drawable) 
		{
			GL gl = drawable.getGL();
			gl.glClearColor(0.0f,0.0f,0.0f,0.0f);
			GLU glu = new GLU();              
			int w = ((Component)drawable).getWidth();
			int h = ((Component)drawable).getHeight();
			int aspect = w/h;
			gl.glMatrixMode(GL.GL_PROJECTION);
			glu.gluPerspective(60.0, aspect, 0.0, 100.0);
		}

		//change what happens when you press keys
		public void moveStuff()
		{			
			//if w is pressed, move player forward
			if(pressed[KeyEvent.VK_W])
				speed = 10;
			else
				speed = 0;

			//if a is pressed, turn left (change walk angle to turn)
			if(pressed[KeyEvent.VK_A])
				walkAng-= 5;

			//if d is pressed, turn right (change walk angle to turn)
			if(pressed[KeyEvent.VK_D])
				walkAng+= 5;
		}

		public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) 
		{

		}

		public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) 
		{

		}
	}

	public class SimpleKeyListener implements KeyListener
	{

		//changes variables while certain key are pressed, allows more than one key to be pressed at a time
		public void keyPressed(KeyEvent e) 
		{
			pressed[e.getKeyCode()] = true;
		}

		//stop changing the variable the key user was pressing changed
		public void keyReleased(KeyEvent e) 
		{
			pressed[e.getKeyCode()] = false;
		}

		public void keyTyped(KeyEvent e) 
		{

		}
	}

	public class SimpleMouseListener implements MouseListener
	{

		public void mouseClicked(MouseEvent e) 
		{
			//if not in the instructions panel
			if(instructions == false)
			{
				//if not in the main game and clicked the instructions button, draw instructions
				if(playPanel == false && e.getX() > 265 && e.getX() < 535 && e.getY() > 275 && e.getY() < 375)
				{
					instructions = true;
					mainMenu.setInst(instructions);
				}
				//if not in the main game and clicked the start button, start the game (get rid of menu
				//panel and add main game panel and initalize players)
				if(playPanel == false && e.getX() > 265 && e.getX() < 535 && e.getY() > 150 && e.getY() < 250)
				{
					anim.stop();
					remove(mainMenu);
					add(animationPanel);
					playPanel = true;
					requestFocus();
					initializeEngine();
				}
			}
			//if i the instructions panel and clicked the back button, return to main menu
			else if(instructions == true && e.getX() > 350 && e.getX() < 450 && e.getY() > 345 && e.getY() < 395)
			{
				instructions = false;
				mainMenu.setInst(instructions);
			}
		}

		public void mouseEntered(MouseEvent e) 
		{

		}

		public void mouseExited(MouseEvent e) 
		{

		}

		public void mousePressed(MouseEvent e) 
		{

		}

		public void mouseReleased(MouseEvent e) 
		{

		}
	}

	private class StepThread implements Runnable
	{
		private static final long stepLength = 1l;
		private long timeTail;
		private long timeLead;

		public void run()
		{
			timeTail = System.currentTimeMillis();
			myField.step();
			while(true)
			{
				timeLead = System.currentTimeMillis();
				if(timeLead >= timeTail + stepLength)
				{
					timeTail = System.currentTimeMillis();
					myField.step();
				}
			}
		}
	}
}
